這系列無障礙的鐵人賽文章,實踐的內容主要是根據 W3C:WAI-ARIA 的實踐,從設計模式及組件(Design Patterns and Widgets)裡面挑選最想嘗試的,如果有朋友想瞭解全部 Widget 該怎麼實作及其規範,歡迎自行爬規範內容,也許我們可以討論一下;若以下文章內容理解有任何錯誤,請多指教~

(圖片來源:unDraw)
3.5 Button
A button is a widget that enables users to trigger an action or event, such as submitting a form, opening a dialog, canceling an action, or performing a delete operation. A common convention for informing users that a button launches a dialog is to append "…" (ellipsis) to the button label, e.g., "Save as…"
按鈕,在一個網站當中,可以說無所不在。它讓使用者可以藉由點擊送出表單、打開對話視窗、取消行為與刪除某操作。最常用的做法是點擊按鈕之後,開啟一個視窗後去做一些事,比如說:存檔、列印...等。
<div> 或 <button> 製作按鈕的差異外觀透過 CSS 都能做到一樣的樣式設定,原生的按鈕元素已具備許多方便鍵盤及螢幕閱讀器操作的特性,以下來介紹有哪些重要觀念:
<div> :需要加上 tablindex="0" ,告訴鍵盤它是可以被操作的。<button>:原生的按鈕元素,在使用者用鍵盤底選 tab 時就能聚焦。div 即使加上 tabindex,變成可聚焦之元素,div 在語義上只是代表一個群組的集合,而 button 卻是代表一個可點按的按鍵,若我們同樣製作一個「註冊」按鈕,使用 VoiceOver 讀出:
<div> :念出「註冊 group」,只表達群組,無法推敲更深層的意義。<button>:念出「註冊 button」,不會造成混淆。假設我們使用 JS 在兩個元素加上監聽,當點擊事件發生時,就 alert 一個視窗,告訴你他被點擊了。
anyButton.addEventListener('click', function () {
	// doSomething(); => 原生按鈕可以被鍵盤的 Enter 正常觸發
})
anyDiv.addEventListener('click', function () {
	// doSomething(); => 假裝的按鈕需要監聽 Keycode
})
<div> :使用滑鼠點擊,正常觸發。透過鍵盤 tab 選擇,再按下 enter,無法反應,需要額外新增的監聽事件,去聽 keycode 是否按下 enter 鍵、空白鍵。<button>:使用滑鼠點擊,正常觸發。透過鍵盤 tab 選擇,再按下 enter,正常觸發!同樣為 div 與 button 加上 disabled 屬性,以及樣式:
<div class="fancy" disabled></div>
<button class="fancy" disabled></button>
.fancy[disabled] {
	background-color: gray;
}
那麼是否可被關注的狀態就該因為 disabled 而被取消,換句話說,這時我們使用 tab 去選網頁上的內容,disabled 屬性的元素是不該被選取到的,會從 focus order 移除,否則將造成使用者非常混亂。
<div> :依然可以 focus。<button>:無法選取。來介紹一下,如果我們要做按鈕組件的一些應用,在 WAI-ARIA 有哪些注意事項。
除了傳統的按鈕組件,WAI-ARIA 還支援另外兩種按鈕類型:
aria-pressed 屬性並指定 true/ false 。aria-pressed="true"來表示「靜音」。
aria-label 代表其標題意義)。在上例中,當按下的狀態為 true 時,標籤保持「靜音」 ,這樣螢幕閱讀器就會朗讀「靜音 toggle button pressed」。換句話說,如果設計要求按鈕標籤在按下後,從「靜音」更改為「未靜音」這種文字內容更新,則不再需要使用 aria-pressed 屬性,念出來會混淆視聽。Menu Button 會和另一個設計模式 Menu / Menu Bar 一起應用,與 aria-haspopup 屬性通力合作。「按鈕」的行為表現和「超連結」不一樣,它們的樣式與角色模型也許都能符合功能要的需求。但是有很多作法是讓有像超連結樣式的元素,行為表現得像按鈕一樣,這時在元素上加上
role="button"可以幫助輔助科技瞭解組件是有什麼功能性。
Alt + U 當作觸發按鈕的快捷鍵,可以移動到上一列的話,焦點就移動到上一列。Alt + U 快速鍵,焦點無法超出清單的範圍,一樣保持在 list 上。button,如果不是原生 <button>,應該要加上 role="button"。aria-label="名稱" 或是 aria-labelledby="某元素id"。aria-describedby="某元素id" 。aria-disabled="true",提醒一下,只有寫 aria-disabled 也是 true 唷。aria-pressed 屬性,值是 true 或 false 。今天想引用 WAI-ARIA Practices 1.1 示範的例子來說明最簡單的按鈕概念,之後銜接 Menu Button 來做一些 RWD 常用的漢堡按鈕的話,也會比較清楚、輕鬆。
以下範例是使用兩個非原生具有 button 語義的標籤來實作按鈕,分別是<div>、<a>,要做的事情是「列印」與「靜音」,因為原範例 svg 路徑的關係,微調一下 JS 寫法。
希望能做到:

<section>
    <p>這是一個 div 按鈕,執行列印功能</p>
    <div tabindex="0"
         role="button"
         id="action">
      Print Page
    </div>
</section>
<section>
    <p>這是一個 a 按鈕,切換靜音與聲音功能</p>
    <a tabindex="0"
       role="button"
       id="toggle"
       aria-pressed="false">
      Mute
        <i class="fas fa-volume-up"></i>
    </a>
</section>
<button>:
role="button"。tab 鍵 focus 的設定, tabindex="0"。<a> ,功能需要切換兩種不同的狀態,所以加上 aria-pressed 屬性,值是 false 代表按下之前是有聲音的,按下之後需要關閉聲音,除了樣式要區隔以外,將 aria-pressed 設為 true 。[role="button"] {
  display: inline-block;
  position: relative;
  padding: 0.4em 0.7em;
  border: 1px solid hsl(213, 71%, 49%);
  border-radius: 5px;
  box-shadow: 0 1px 2px hsl(216, 27%, 55%);
  color: #fff;
  text-shadow: 0 -1px 1px hsl(216, 27%, 25%);
  background-color: hsl(216, 82%, 51%);
  background-image: linear-gradient(to bottom, hsl(216, 82%, 53%), hsl(216, 82%, 47%));
}
[role="button"]:hover {
  border-color: hsl(213, 71%, 29%);
  background-color: hsl(216, 82%, 31%);
  background-image: linear-gradient(to bottom, hsl(216, 82%, 33%), hsl(216, 82%, 27%));
  cursor: default;
}
[role="button"]:focus {
  outline: none;
}
[role="button"]:focus::before {
  position: absolute;
  z-index: -1;
  /* button border width - outline width - offset */
  top: calc(-1px - 3px - 3px);
  right: calc(-1px - 3px - 3px);
  bottom: calc(-1px - 3px - 3px);
  left: calc(-1px - 3px - 3px);
  border: 3px solid hsl(213, 71%, 49%);
  /* button border radius + outline width + offset */
  border-radius: calc(5px + 3px + 3px);
  content: '';
}
[role="button"]:active {
  border-color: hsl(213, 71%, 49%);
  background-color: hsl(216, 82%, 31%);
  background-image: linear-gradient(to bottom, hsl(216, 82%, 53%), hsl(216, 82%, 47%));
  box-shadow: inset 0 3px 5px 1px hsl(216, 82%, 30%);
}
[role="button"][aria-pressed] {
    /* 這裡只是幫有需要切換狀態的範例加上新的顏色(紫色)做為區別 */
  border-color: hsl(261, 71%, 49%);
  box-shadow: 0 1px 2px hsl(261, 27%, 55%);
  text-shadow: 0 -1px 1px hsl(261, 27%, 25%);
  background-color: hsl(261, 82%, 51%);
  background-image: linear-gradient(to bottom, hsl(261, 82%, 53%), hsl(261, 82%, 47%));
}
[role="button"][aria-pressed]:hover {
    /* 這裡只是幫有需要切換狀態的範例加上新的顏色(紫色)做為區別 */
  border-color: hsl(261, 71%, 29%);
  background-color: hsl(261, 82%, 31%);
  background-image: linear-gradient(to bottom, hsl(261, 82%, 33%), hsl(261, 82%, 27%));
}
[role="button"][aria-pressed]:focus::before {
    /* 這裡只是幫有需要切換狀態的範例加上新的顏色(紫色)做為區別 */
  border-color: hsl(261, 71%, 49%);
}
/* 開始為狀態加上樣式 */
[role="button"][aria-pressed="true"] {
  padding-top: 0.5em;
  padding-bottom: 0.3em;
  border-color: hsl(261, 71%, 49%);
  background-color: hsl(261, 82%, 31%);
  background-image: linear-gradient(to bottom, hsl(261, 82%, 63%), hsl(261, 82%, 57%));
  box-shadow: inset 0 3px 5px 1px hsl(261, 82%, 30%);
}
[role="button"][aria-pressed="true"]:hover {
  border-color: hsl(261, 71%, 49%);
  background-color: hsl(261, 82%, 31%);
  background-image: linear-gradient(to bottom, hsl(261, 82%, 43%), hsl(261, 82%, 37%));
  box-shadow: inset 0 3px 5px 1px hsl(261, 82%, 20%);
}    
:hover
:active
:focus
[role="button"][aria-pressed="true"] 。最後,codepen 在這裏,快來使用鍵盤操作看看吧!結論是,除了 <button> 外,還是可以使用 <div> 或是 <a> 來製作一個按鈕,不過需要幫忙補充語義及 JS 要做的事情都比較多(監聽各種 keycode,注意事項請參考上文),大家就自行斟酌囉。